Skip to content

Add functions to get original file name and loop device info#58

Open
skorobogatydmitry wants to merge 9 commits intostratis-storage:masterfrom
skorobogatydmitry:sd/ld-names
Open

Add functions to get original file name and loop device info#58
skorobogatydmitry wants to merge 9 commits intostratis-storage:masterfrom
skorobogatydmitry:sd/ld-names

Conversation

@skorobogatydmitry
Copy link

@skorobogatydmitry skorobogatydmitry commented Jul 6, 2025

It could be useful to be able to get original file name and info of a loop device in case the original LoopDevice isn't available for any reason.

I have some more changes in mind like listing devices, applying LoopConfig instead of calling attach, etc

Testing

All tests worked but add_a_loop_device.
I don't think it's related to the change.

Summary by CodeRabbit

  • New Features
    • Loop devices now expose the original backing file path when attached using a file path.
    • Added a method to retrieve current loop device status/info.
    • Note: When attached via file descriptor, the original path is not available.
  • Tests
    • Added integration tests validating original path reporting (present/absent) and successful info retrieval across attachment methods.

@packit-as-a-service
Copy link

Congratulations! One of the builds has completed. 🍾

You can install the built RPMs by following these steps:

  • sudo yum install -y dnf-plugins-core on RHEL 8
  • sudo dnf install -y dnf-plugins-core on Fedora
  • dnf copr enable packit/stratis-storage-loopdev-3-58
  • And now you can install the packages.

Please note that the RPMs should be used only in a testing environment.

@mulkieran
Copy link
Member

@skorobogatydmitry Thanks for PR. It looks like you hit some lints in your submitted code that you should fix. You should be able to force push. We have a Makefile target clippy for lints and also a fmt target for formatting. Best to run both of those.

@mulkieran mulkieran self-requested a review July 7, 2025 13:00
@mulkieran mulkieran moved this to In Progress in 2025June Jul 7, 2025
@mulkieran mulkieran removed this from 2025June Jul 8, 2025
@mulkieran mulkieran moved this to In Progress in 2025July Jul 8, 2025
@skorobogatydmitry
Copy link
Author

@skorobogatydmitry Thanks for PR. It looks like you hit some lints in your submitted code that you should fix. You should be able to force push. We have a Makefile target clippy for lints and also a fmt target for formatting. Best to run both of those.

I hope the change is ok now. LMK if something else isn't fit.

Some context on why I want to contribute.
That's the most active fork of the original crate. I need this functionality in my pet-project and already have some code locally in it, but really like to submit it to an upstream.

Copy link
Member

@mulkieran mulkieran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two preliminary requests...please put the requested code up in a separate PR and I'll merge that immediately, then resume reviewing this PR.

src/lib.rs Outdated
loop_info64, LOOP_CLR_FD, LOOP_CTL_ADD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD,
LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY,
};
use bindings::LOOP_GET_STATUS64;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not put this use in with the other collected uses from bindings on line 40-43?

@mulkieran
Copy link
Member

@skorobogatydmitry This crate is the official successor of the original loopdev crate (https://rustsec.org/advisories/RUSTSEC-2023-0088.html). We took it over because it was no longer compiling and the original author was unresponsive. However, since we are not the original author, review will take a little bit longer than with a code base we are familiar with. You will have to bear with us.

let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;

// store backing file name in the info
let name = loname::Name::from_path(&backing_file).unwrap_or_default();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the purpose of this but I'm unsure why you need the special Name struct for marshalling and unmarshalling. I feel that it would be better to use existing Path and PathBuf methods, as_os_str() or something. Can you tell me why the concern w/ null terminators in the to_string method? Thanks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern was compatibility with C code.
I found that kernel actually takes care of the terminator here.

I did a brief check that name setup through as_os_str is correctly displayed by losetup -l, but that's not a reliable source, because ...

losetup actually leverages cat /sys/block/loop0/loop/backing_file on my system. It isn't limited at 64 chars and a way more straight-forward path.

I think I need to get rid of lo_file_name logic but keep info around for future use.

Do you think it's reasonable ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting the full name from /sys/block/loop0/loop/backing_file isn't that simple, as it requires to know the loop0 part, which, in its turn, requires an info call. The only real advantage is that it allows to get the full name in all cases.
LMK if you want to see the version with as_os_str anyways

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would be for removing some of the more complicated handling that @mulkieran mentioned. I can see something like a length check potentially being necessary, but I believe that if we're doing FFI, it's better to use standard ways of null-encoding the string. Custom logic with null handling has caused me a lot of pain in the past.

Copy link
Author

@skorobogatydmitry skorobogatydmitry Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the point of removing the custom logic for handling \0, let me find a way to make it with OsString.
However, I would rather keep the naming helper separated from the rest of the code so it doesn't pollute the lib.rs, which is nearing the limit of a good code unit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended-up with casting &[u8] -> CStr (because it's actually a CStr) -> OsStr (because it's needed to make a PathBuf) -> PathBuf.
So, there's no Tostring anymore, although the code is even more complicated.

Copy link
Member

@mulkieran mulkieran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one semantic question so far, still working...

// store backing file name in the info
let name = loname::Name::from_path(&backing_file).unwrap_or_default();
info.lo_file_name = name.0;
info.lo_crypt_name = name.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider only setting lo_file_name and not lo_crypt_name here? If not, are you using io_crypt_name? I've made some preliminary inquiries and it seems not entirely clear what it is for.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider only setting lo_file_name and not lo_crypt_name here?
Sure, will do.

I don't use lo_crypt_name, but I found that losetup sets both and they're semantically similar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally okay with mimicking losetup if you are @mulkieran

impl Name {
pub fn from_path<Y: AsRef<Path>>(path: Y) -> Result<Self, String> {
let s = path.as_ref().as_os_str().as_encoded_bytes();
if s.len() > LO_NAME_SIZE as usize {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned that this use of .len() is not correct, because it means the bytes the implementation has allocated, not the actual length of the characters required to encode the path. I'm going to ask another person to look at this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LO_NAME_SIZE comes from C header and limits number of bytes, AFAIU. So the comparison should be right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, I believe that .len() is correct. You may be thinking of .capacity().

@mulkieran
Copy link
Member

@skorobogatydmitry Please rebase and force push when you get the chance.

@skorobogatydmitry
Copy link
Author

@skorobogatydmitry Please rebase and force push when you get the chance.

Done.
It's not a good practice, IMO

@mulkieran mulkieran removed this from 2025July Aug 8, 2025
@mulkieran mulkieran moved this to In Review in 2025August Aug 8, 2025
@mulkieran mulkieran added this to the 0.5.3 milestone Aug 13, 2025
@mulkieran
Copy link
Member

Build failures seem infrastructure related, we can ignore.

@mulkieran mulkieran moved this to In Review in 2025October Oct 11, 2025
@mulkieran mulkieran modified the milestones: 0.5.3, 0.5.4 Oct 31, 2025
@mulkieran mulkieran removed this from 2025October Nov 17, 2025
@mulkieran mulkieran moved this to In Review in 2025November Nov 17, 2025
@mulkieran mulkieran moved this from In Review to Pending in 2025November Dec 1, 2025
@mulkieran mulkieran moved this from Pending to In Progress (long term) in 2025November Dec 1, 2025
@mulkieran mulkieran removed this from 2025November Dec 1, 2025
@mulkieran mulkieran moved this to In Progress (long term) in 2025December Dec 1, 2025
@mulkieran mulkieran moved this to In Progress (long term) in 2026January Jan 2, 2026
@mulkieran mulkieran removed this from 2025December Jan 2, 2026
Copy link
Member

@jbaublitz jbaublitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mulkieran I've provided my review. @skorobogatydmitry I apologize that it's taken me so long to get around to this. Are you interested in resuming work on this?

impl Name {
pub fn from_path<Y: AsRef<Path>>(path: Y) -> Result<Self, String> {
let s = path.as_ref().as_os_str().as_encoded_bytes();
if s.len() > LO_NAME_SIZE as usize {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, I believe that .len() is correct. You may be thinking of .capacity().

src/lib.rs Outdated
Comment on lines 430 to 432
let ptr = Box::into_raw(empty_info);
let ret_code = libc::ioctl(fd.as_raw_fd(), LOOP_GET_STATUS64 as IoctlRequest, ptr);
ioctl_to_error(ret_code).map(|_| *Box::from_raw(ptr))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we're not just using .as_ptr() or .as_mut_ptr() here?

Copy link
Author

@skorobogatydmitry skorobogatydmitry Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason, I just used what I knew. Will change.
I also this thank using as_mut_ptr() eliminates one of the leakages pointed out in the AI review.

It's in nightly: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.as_mut_ptr
I assume it's not ok to use, as the crate has rust 1.74.0 pinned.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, you're right. I believe it would still be better to use something like .as_ref() or .as_mut() as needed and then cast it using as to a pointer. I'm also a bit confused as to why we need the box at all. I'm not seeing a clear reason why we would need a heap allocation when at the end of the method, we copy the data out of the Box and pass it back by value as the return type. If we passed back the box I could maybe see why we might do that but as it stands, I think it would work just as well with a stack allocation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, actually. Let me get rid of Box-es.

// store backing file name in the info
let name = loname::Name::from_path(&backing_file).unwrap_or_default();
info.lo_file_name = name.0;
info.lo_crypt_name = name.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally okay with mimicking losetup if you are @mulkieran

let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;

// store backing file name in the info
let name = loname::Name::from_path(&backing_file).unwrap_or_default();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would be for removing some of the more complicated handling that @mulkieran mentioned. I can see something like a length check potentially being necessary, but I believe that if we're doing FFI, it's better to use standard ways of null-encoding the string. Custom logic with null handling has caused me a lot of pain in the past.

@mulkieran mulkieran removed this from 2026January Feb 17, 2026
@mulkieran mulkieran moved this to In Progress (long term) in 2026February Feb 17, 2026
@mulkieran mulkieran moved this from In Progress (long term) to In Review in 2026February Feb 17, 2026
@skorobogatydmitry skorobogatydmitry marked this pull request as draft February 18, 2026 13:56
@skorobogatydmitry
Copy link
Author

I'm playing around with the way to produce a PathBuf from [u8;64]. Will change PR status after pushing the changes.
Thank you for looking at the code!

@packit-as-a-service
Copy link

Congratulations! One of the builds has completed. 🍾

You can install the built RPMs by following these steps:

  • sudo dnf install -y 'dnf*-command(copr)'
  • dnf copr enable packit/stratis-storage-loopdev-3-58-copr_pull
  • And now you can install the packages.

Please note that the RPMs should be used only in a testing environment.


impl TryInto<PathBuf> for Name {
type Error = NameToPathBufError;
fn try_into(self) -> Result<PathBuf, Self::Error> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm exposing possible errors here, but the outer scope supresses it, as I think it's a meaningful action on that level.

/// # Return
/// None - path field is empty, not a valid path or ioctl error was encountered
/// Some(PathBuf) - otherwise
pub fn original_path(&self) -> Option<PathBuf> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this has to be replaced by this:

        self.info().ok().and_then(|info| {
            self.path()
                .and_then(|dev_loop_path| {
                    dev_loop_path.file_name().and_then(|dev_name| {
                        fs::read_to_string(
                            PathBuf::from("/sys/block")
                                .join(dev_name)
                                .join("loop/backing_file"),
                        )
                        .ok()
                    })
                })
                .map(|string| PathBuf::from(string))
                .or(Name(info.lo_file_name).try_into().ok())
        })

but would like to see a thumb-up on this version

@skorobogatydmitry skorobogatydmitry marked this pull request as ready for review February 20, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

3 participants